summaryrefslogtreecommitdiff
path: root/app/[lng]/evcp/(evcp)/gtc/[id]/page.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'app/[lng]/evcp/(evcp)/gtc/[id]/page.tsx')
-rw-r--r--app/[lng]/evcp/(evcp)/gtc/[id]/page.tsx160
1 files changed, 122 insertions, 38 deletions
diff --git a/app/[lng]/evcp/(evcp)/gtc/[id]/page.tsx b/app/[lng]/evcp/(evcp)/gtc/[id]/page.tsx
index 0f783375..20f0ea51 100644
--- a/app/[lng]/evcp/(evcp)/gtc/[id]/page.tsx
+++ b/app/[lng]/evcp/(evcp)/gtc/[id]/page.tsx
@@ -6,14 +6,20 @@ import { Skeleton } from "@/components/ui/skeleton"
import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
import { Shell } from "@/components/shell"
import { InformationButton } from "@/components/information/information-button"
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
+import { Badge } from "@/components/ui/badge"
+import { FileText, Calendar, AlertTriangle, ArrowLeft } from "lucide-react"
+import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbSeparator, BreadcrumbPage } from "@/components/ui/breadcrumb"
+import { formatDateTime } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import Link from "next/link"
-import {
- getGtcClauses,
+import {
+ getGtcClauses,
getUsersForFilter,
} from "@/lib/gtc-contract/gtc-clauses/service"
import { getGtcDocumentById } from "@/lib/gtc-contract/service"
import { searchParamsCache } from "@/lib/gtc-contract/gtc-clauses/validations"
-import { GtcClausesPageHeader } from "@/lib/gtc-contract/gtc-clauses/gtc-clauses-page-header"
import { GtcClausesTable } from "@/lib/gtc-contract/gtc-clauses/table/clause-table"
interface GtcClausesPageProps {
@@ -51,38 +57,38 @@ export default async function GtcClausesPage(props: GtcClausesPageProps) {
return (
<Shell className="gap-2">
- {/* 헤더 컴포넌트 */}
- <GtcClausesPageHeader document={document} />
-
- {/* 문서 정보 카드 */}
- <div className="rounded-lg border bg-card p-4">
- <div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
- <div>
- <div className="font-medium text-muted-foreground">최초등록일</div>
- <div>{document.createdAt ? new Date(document.createdAt).toLocaleDateString('ko-KR') : '-'}</div>
- </div>
- <div>
- <div className="font-medium text-muted-foreground">최초등록자</div>
- <div>{document.createdByName || '-'}</div>
- </div>
- <div>
- <div className="font-medium text-muted-foreground">최종수정일</div>
- <div>{document.updatedAt ? new Date(document.updatedAt).toLocaleDateString('ko-KR') : '-'}</div>
- </div>
- <div>
- <div className="font-medium text-muted-foreground">최종수정자</div>
- <div>{document.updatedByName || '-'}</div>
- </div>
- </div>
-
- {document.editReason && (
- <div className="mt-3 pt-3 border-t">
- <div className="font-medium text-muted-foreground mb-1">최종 편집사유</div>
- <div className="text-sm">{document.editReason}</div>
- </div>
- )}
+
+ <div className="flex items-center justify-between">
+ <Breadcrumb>
+ <BreadcrumbList>
+ <BreadcrumbItem>
+ <BreadcrumbLink href="/evcp">EVCP</BreadcrumbLink>
+ </BreadcrumbItem>
+ <BreadcrumbSeparator />
+ <BreadcrumbItem>
+ <BreadcrumbLink href="/evcp/gtc">GTC 목록 관리</BreadcrumbLink>
+ </BreadcrumbItem>
+ <BreadcrumbSeparator />
+ <BreadcrumbItem>
+ <BreadcrumbPage>GTC 조항 관리</BreadcrumbPage>
+ </BreadcrumbItem>
+ </BreadcrumbList>
+ </Breadcrumb>
+
+ <Button asChild variant="outline" size="sm">
+ <Link href="/evcp/basic-contract">
+ <ArrowLeft className="h-4 w-4 mr-2" />
+ 목록으로 돌아가기
+ </Link>
+ </Button>
</div>
+ {/* 템플릿 정보 섹션 (콤팩트) */}
+ <React.Suspense fallback={<TemplateInfoSkeleton />}>
+ <DocumentInfo document={document} />
+ </React.Suspense>
+
+
{/* 조항 테이블 */}
<React.Suspense
fallback={
@@ -96,7 +102,7 @@ export default async function GtcClausesPage(props: GtcClausesPageProps) {
}
>
<GtcClausesTable
- promises={promises}
+ promises={promises}
documentId={documentId}
document={document}
/>
@@ -109,7 +115,7 @@ export default async function GtcClausesPage(props: GtcClausesPageProps) {
export async function generateMetadata(props: GtcClausesPageProps) {
const params = await props.params
const documentId = parseInt(params.id)
-
+
if (isNaN(documentId)) {
return {
title: "GTC 조항 관리",
@@ -118,7 +124,7 @@ export async function generateMetadata(props: GtcClausesPageProps) {
try {
const document = await getGtcDocumentById(documentId)
-
+
if (!document) {
return {
title: "GTC 조항 관리",
@@ -126,7 +132,7 @@ export async function generateMetadata(props: GtcClausesPageProps) {
}
const title = `GTC 조항 관리 - ${document.type === "standard" ? "표준" : "프로젝트"} v${document.revision}`
- const description = document.project
+ const description = document.project
? `${document.project.name} (${document.project.code}) 프로젝트의 GTC 조항을 관리합니다.`
: "표준 GTC 조항을 관리합니다."
@@ -139,4 +145,82 @@ export async function generateMetadata(props: GtcClausesPageProps) {
title: "GTC 조항 관리",
}
}
-} \ No newline at end of file
+}
+
+
+async function DocumentInfo({
+ document,
+}: {
+ document: Promise<Awaited<ReturnType<typeof getGtcDocumentById>>>
+}) {
+ const documentInfo = await document
+
+ if (!documentInfo) {
+ return (
+ <Card>
+ <CardContent className="py-6">
+ <div className="text-center text-gray-500">
+ <AlertTriangle className="h-8 w-8 mx-auto mb-2" />
+ <p>템플릿 정보를 찾을 수 없습니다.</p>
+ </div>
+ </CardContent>
+ </Card>
+ )
+ }
+
+ return (
+ <Card>
+ <CardHeader className="py-4">
+ <div className="flex flex-wrap items-center justify-between gap-2">
+ {/* 좌측: 제목 + 리비전 */}
+ <div className="flex items-center gap-2 min-w-0">
+ <CardTitle className="text-lg font-semibold leading-none truncate">
+ {documentInfo.title}
+ </CardTitle>
+ <Badge variant="outline" className="shrink-0">v{documentInfo.revision}</Badge>
+ </div>
+
+ <div className="flex items-center gap-2">
+ <Badge
+ variant={documentInfo.isActive ? "default" : "secondary"}
+ className="shrink-0"
+ >
+ {documentInfo.isActive ? "활성" : "비활성"}
+ </Badge>
+ </div>
+ </div>
+
+ {/* 메타 정보 한 줄 정리 */}
+ <div className="mt-1 flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
+ <span className="inline-flex items-center gap-1">
+ <Calendar className="h-3.5 w-3.5" />
+ 생성일: {formatDateTime(documentInfo.createdAt, "KR")}
+ </span>
+ {documentInfo.fileName && (
+ <>
+ <span className="select-none">•</span>
+ <span className="inline-flex items-center gap-1 min-w-0">
+ <FileText className="h-3.5 w-3.5" />
+ <span className="truncate max-w-[52ch]">템플릿 파일: {documentInfo.fileName}</span>
+ </span>
+ </>
+ )}
+ </div>
+ </CardHeader>
+ </Card>
+ )
+}
+
+// 로딩 스켈레톤 (콤팩트)
+function TemplateInfoSkeleton() {
+ return (
+ <Card>
+ <CardHeader className="py-4">
+ <div className="space-y-2">
+ <div className="h-5 bg-gray-200 rounded w-1/2 animate-pulse" />
+ <div className="h-3 bg-gray-200 rounded w-1/3 animate-pulse" />
+ </div>
+ </CardHeader>
+ </Card>
+ )
+}